﻿using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Memory;

namespace NetMicro.Cache.InMemoryProvider
{

    public class MemoryCache : IInMemoryCache
    {
        private readonly IMemoryCache _cache;

        public MemoryCache(IMemoryCache cache)
        {
            _cache = cache;
        }

        public string Get(string key)
        {
            object value = _cache.Get(key);
            return value?.ToString();
        }

        public T Get<T>(string key)
        {
            return _cache.Get<T>(key);
        }

        public Task<string> GetAsync(string key)
        {
            string value = Get(key);
            return Task.FromResult(value);
        }

        public Task<T> GetAsync<T>(string key)
        {
            return Task.FromResult(_cache.Get<T>(key));
        }

        public bool TryGetValue(string key, out string value)
        {
            return _cache.TryGetValue(key, out value);
        }

        public bool TryGetValue<T>(string key, out T value)
        {
            return _cache.TryGetValue(key, out value);
        }

        public bool Set<T>(string key, T value)
        {
            _cache.Set(key, value);
            return true;
        }

        public bool Set<T>(string key, T value, int expires)
        {
            _cache.Set(key, value, new TimeSpan(0, 0, 0, expires));
            return true;
        }

        public Task<bool> SetAsync<T>(string key, T value)
        {
            Set(key, value);
            return Task.FromResult(true);
        }

        public Task<bool> SetAsync<T>(string key, T value, int expires)
        {
            Set(key, value, expires);
            return Task.FromResult(true);
        }

        public bool Remove(string key)
        {
            _cache.Remove(key);
            return true;
        }

        public Task<bool> RemoveAsync(string key)
        {
            _cache.Remove(key);
            return Task.FromResult(true);
        }

        public bool Exists(string key)
        {
            return TryGetValue(key, out _);
        }

        public Task<bool> ExistsAsync(string key)
        {
            return Task.FromResult(TryGetValue(key, out _));
        }

        #region Distributed Locks

        private static readonly ConcurrentDictionary<string, object> _LockCache = new ConcurrentDictionary<string, object>();
        private static readonly ConcurrentDictionary<string, string> _LockUserCache = new ConcurrentDictionary<string, string>();

        /// <summary>
        /// 使用锁执行一个函数
        /// </summary>
        /// <param name="key">锁的键</param>
        /// <param name="millisecondsTimeout">等待时间（毫秒）</param>
        /// <param name="executeAction">要执行的函数（委托）</param>
        public void Lock(string key, int millisecondsTimeout, Action executeAction = null)
        {
            if (executeAction == null)
            {
                return;
            }
            string token = Guid.NewGuid().ToString(); //释放令牌
            if (LockTake(key, token, millisecondsTimeout))
            {
                try
                {
                    executeAction();
                }
                finally
                {
                    LockRelease(key, token);
                }
            }
        }

      

        /// <summary>
        /// 使用锁执行一个方法
        /// </summary>
        /// <typeparam name="T">返回值类型</typeparam>
        /// <param name="key">锁的键</param>
        /// <param name="millisecondsTimeout">等待时间（毫秒）</param>
        /// <param name="executeAction">要执行的方法</param>
        /// <param name="defaultValue">默认返回</param>
        /// <returns></returns>
        public T Lock<T>(string key, int millisecondsTimeout, Func<T> executeAction, T defaultValue = default(T))
        {
            if (executeAction == null) return defaultValue;
            string token = Guid.NewGuid().ToString(); //释放令牌
            if (LockTake(key, token, millisecondsTimeout))
            {
                try
                {
                    T result = executeAction();
                    return result;
                }
                finally
                {
                    LockRelease(key, token);
                }
            }
            return defaultValue;
        }

        /// <summary>
        /// 获取一个锁(需要自己释放)
        /// </summary>
        /// <param name="key">锁的键</param>
        /// <param name="value">当前占用值</param>
        /// <param name="span">等待时间（毫秒）</param>
        /// <returns>成功返回true</returns>
        public bool LockTake(string key, string value, int millisecondsTimeout)
        {
            object obj = _LockCache.GetOrAdd(key, new object());
            if (Monitor.TryEnter(obj, millisecondsTimeout))
            {
                _LockUserCache[key] = value;
                return true;
            }
            return false;
        }

        /// <summary>
        /// 释放一个锁
        /// </summary>
        /// <param name="key">锁的键</param>
        /// <param name="value">当前占用值</param>
        /// <returns>成功返回true</returns>
        public bool LockRelease(string key, string value)
        {
            object obj;
            _LockCache.TryGetValue(key, out obj);
            if (obj != null)
            {
                if (_LockUserCache[key] == value)
                {
                    Monitor.Exit(obj);
                    return true;
                }
                return false;
            }
            return true;
        }
        #endregion
    }
}
